import * as THREE from 'three';

import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { getAddress, getFakeData, getLines, getRandomInt, worldToScreen } from '@/core/tooler';
import { Line2 } from 'three/examples/jsm/lines/Line2.js'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
// import { MeshLine, MeshLineMaterial, MeshLineRaycast } from 'three.meshline';

import { MeshLine, MeshLineMaterial } from "./line.js"

let group;
let topGroup;
let container, stats;
let camera, scene, renderer;
let topScene;
let selectedObject = null;
let overlay;

let pointMap;
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();

const config = {
    pointerColor: 0xff0000,
    hoverColor: 0xff9900
}


export function init(canvas: any, $overlay: any) {
    overlay = $overlay;
    container = document.body;

    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 20000);
    camera.position.z = 2000;

    const controls = new OrbitControls(camera, container);
    controls.minDistance = 100;
    controls.maxDistance = 30000;

    scene = new THREE.Scene();
    scene.fog = new THREE.FogExp2(0x000000, 0.0004);

    topScene = new THREE.Scene();
    topScene.fog = new THREE.FogExp2(0x000000, 0.0004);

    group = new THREE.Group();
    scene.add(group);

    topGroup = new THREE.Group();
    topScene.add(topGroup);

    renderer = new THREE.WebGLRenderer({ antialias: true, canvas });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.autoClear = false;

    stats = new Stats();
    container.appendChild(stats.dom);

    window.addEventListener('resize', onWindowResize);
    document.addEventListener('pointermove', onPointerMove);

    addView(getFakeData(4));

    // addMeshLine();
    addBoxFromPoint(new THREE.Vector3(0, 100, 0), new THREE.Vector3(300, 100, 500));

    topScene.add(new THREE.AxesHelper(100))

    animate();
}

function addBoxFromPoint(point1: THREE.Vector3, point2: THREE.Vector3) {
    //     const point1 = new THREE.Vector3(0, 0, 0);  // 第一个点的坐标
    // const point2 = new THREE.Vector3(2, 3, 4);  // 第二个点的坐标
    const size = 4;
    const distance = point1.distanceTo(point2);
    const geometry = new THREE.BoxGeometry(size, size, distance);

    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    const box = new THREE.Mesh(geometry, material);

    // const midpoint = point1.clone().add(point2).multiplyScalar(0.5);
    // box.position.copy(midpoint);
    let center = new THREE.Vector3((point1.x + point2.x) / 2, (point1.y + point2.y) / 2, (point1.z + point2.z) / 2);

    box.position.set(center.x, center.y, center.z)

    const tempVector = new THREE.Vector3().subVectors(point1, point2);


    box.lookAt(tempVector);
    // box.lookAt(point2)
    addGeo(material, point1);
    addGeo(material, point2);
    

    topGroup.add(box);  // 将框添加到场景中进行渲染

}

function addGeo(material:any, p:THREE.Vector3){
    let geo = new THREE.SphereGeometry(8);
    let mesh = new THREE.Mesh(geo, material);
    mesh.position.set(p.x, p.y, p.z);
    topGroup.add(mesh);
}

function addMeshLine() {
    const material = new MeshLineMaterial({
        transparent: true,
        lineWidth: 4,
        color: new THREE.Color(0xff5500),
        dashArray: 2,     // always has to be the double of the line
        dashOffset: 0,    // start the dash at zero
        dashRatio: 0.75,  // visible length range min: 0.99, max: 0.5
    });

    const points = [];
    let r = 300;
    for (let j = 0; j < Math.PI; j += 2 * Math.PI / 100) {
        points.push(new THREE.Vector3(Math.cos(j) * r, Math.sin(j) * r, 0));
    }
    const geometry = new THREE.BufferGeometry().setFromPoints(points);
    const line = new MeshLine();
    line.setGeometry(geometry);
    const mesh = new THREE.Mesh(line, material);
    console.log("mesh", mesh)
    topScene.add(mesh);
}

function addView(data) {
    const points = getAddress(data);
    const lines = getLines(data);
    pointMap = addPoints(points);
    addLines(lines, pointMap);
}

function addPoints(points: any[]) {
    const sprite = new THREE.TextureLoader().load('./asset/img/pot.png');
    const map = {};
    for (let i = 0; i < points.length; i++) {
        const pot = new THREE.Sprite(new THREE.SpriteMaterial({ color: config.pointerColor, map: sprite, sizeAttenuation: false, transparent: true, fog: true }));
        let size = 800;
        let item = pot;
        let x = getRandomInt(-size, size);
        let y = getRandomInt(-size, size);
        let z = getRandomInt(-size, size);
        let r = getRandomInt(1, 6) * 0.01;
        item.position.set(x, y, z);
        item.scale.set(r, r, r);
        topGroup.add(item);
        map[points[i]] = item;
    }
    return map;
}

function addLines(lines: any[], map: any) {
    const material = new THREE.LineDashedMaterial({ color: 0xffffff, transparent: true, opacity: 0.4, dashSize: 4, gapSize: 2 });
    const matThresholdLine = new LineMaterial({
        color: 0xffffff,
        linewidth: 4, // in world units with size attenuation, pixels otherwise
        worldUnits: true,
        // vertexColors: true,
        transparent: true,
        opacity: 1,
        depthTest: true,
        visible: true,
        //resolution:  // to be set by renderer, eventually

    });

    lines.forEach(item => {
        const points = [];
        const toObj = map[item.to];
        const fromObj = map[item.from];
        points.push(toObj.position);
        points.push(fromObj.position);
        const geometry = new THREE.BufferGeometry();
        geometry.setFromPoints(points);
        const line = new THREE.LineSegments(geometry, material);
        line.computeLineDistances();
        scene.add(line);

        addBoxFromPoint(fromObj.position, toObj.position);


        // const toObj = map[item.to];
        // const fromObj = map[item.from];

        // const colors = [];
        // const color = new THREE.Color(0xff0000);
        // colors.push( color.r, color.g, color.b );
        // colors.push( color.r, color.g, color.b );
        // let positions = [];
        // positions.push(fromObj.position.x, fromObj.position.y, fromObj.position.z)
        // positions.push(toObj.position.x, toObj.position.y, toObj.position.z)
        // const lineGeometry = new LineGeometry();
        // lineGeometry.setPositions( positions );
        // lineGeometry.setColors( colors );
        // const line = new Line2( lineGeometry, matThresholdLine );
        // line.computeLineDistances();
        // line.scale.set( 1, 1, 1 );
        // scene.add(line);
    })
}

// 更新 DOM 元素的位置
function updateOverlayPosition() {
    if (!selectedObject) {
        overlay.style.transform = `translate(-200%, -200%)`;
        return;
    }
    const vector = worldToScreen(selectedObject, camera);
    const widthHalf = window.innerWidth / 2;
    const heightHalf = window.innerHeight / 2;
    const x = (vector.x * widthHalf) + widthHalf;
    const y = -(vector.y * heightHalf) + heightHalf;
    overlay.style.transform = `translate(-50%, -50%) translate(${x}px, ${y}px)`;
}


function onPointerMove(event) {
    if (selectedObject) {
        selectedObject.material.color.set(config.pointerColor);
        selectedObject = null;
    }

    pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
    pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;

    raycaster.setFromCamera(pointer, camera);

    const intersects = raycaster.intersectObject(topGroup, true);

    if (intersects.length > 0) {

        const res = intersects.filter(function (res) {

            return res && res.object;

        })[0];

        if (res && res.object) {
            for (let i in pointMap) {
                if (pointMap[i] == res.object) {
                    overlay.innerText = i;
                    break;
                }
            }
            selectedObject = res.object;
            selectedObject.material.color.set(config.hoverColor);

        }

    }

}

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
    requestAnimationFrame(animate);
    stats.update();
    updateOverlayPosition();
    render();
}

function render() {
    const time = Date.now() * 0.001;

    // group.rotation.y = time * 0.03;
    // topGroup.rotation.y = time * 0.03;

    renderer.clear();
    renderer.render(scene, camera);
    renderer.clearDepth();
    renderer.render(topScene, camera);
}